﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Navigation;
using System.Windows.Data;


namespace DailyFx.UI
{
	/// <summary>
	///		A service that manages windows, ( IUIVisualizerService in CinchV2, plays the same role ).
	/// </summary>
	public interface IWindowManager
	{
		/// <summary>
		/// Shows a non-model window for the specified model.
		/// </summary>
		void ShowWindow ( object model, object context = null, IDictionary<string, object> settings = null);

		/// <summary>
		///		Shows a model dialog for the specified model.
		/// </summary>
		bool? ShowDialog ( object model, object context = null, IDictionary<string, object> settings = null);

		/// <summary>
		///		Shows a popup at the current mouse position.
		/// </summary>
		void ShowPopup ( object model, object context = null, IDictionary<string, object> settings = null);
	}

	/// <summary>
	/// 
	/// </summary>
	public class WindowManager : IWindowManager
	{
		/// <summary>
		///		Shows a window for the specified model.
		/// </summary>
		public virtual void ShowWindow ( object model, object context = null, IDictionary<string, object> settings = null)
		{
			NavigationWindow nav = null;

			if (Application.Current != null && Application.Current.MainWindow != null)
			{
				nav = Application.Current.MainWindow as NavigationWindow;
			}

			if (nav != null)
			{
				var window = CreatePage (model, context, settings);
				nav.Navigate( window);
			}
			else
			{
				CreateWindow ( model, false, context, settings).Show();
			}
		}

		/// <summary>
		///		Shows a model dialog for the specified model.
		/// </summary>
		public virtual bool? ShowDialog ( object model, object context = null, IDictionary<string, object> settings = null)
		{
			return CreateWindow( model, true, context, settings).ShowDialog();
		}

		/// <summary>
		///		Shows a popup at the current mouse position.
		/// </summary>
		public virtual void ShowPopup ( object model, object context = null, IDictionary<string, object> settings = null)
		{
			var popup = CreatePopup( model, settings );
			var view = View.CreateOrRetrieveViewElement ( model, popup, context);

			popup.Child = view;
			popup.SetValue( View.IsCreatedProperty, true);

			Bind.SetBinding( model, popup, null );
			ActionProps.SetUncontextTarget ( view, model );

			var enabledDisabled = model as IUIStatus;
			if ( enabledDisabled != null)
			{
				enabledDisabled.SetEnabled();
				popup.Closed += delegate { enabledDisabled.SetDisabled(true); };
			}

			popup.IsOpen = true ;
			popup.CaptureMouse();
		}


		 /// <summary>
        ///		Creates a window.
        /// </summary>
        protected virtual System.Windows.Window CreateWindow ( object model, bool box, object context, IDictionary<string, object> settings)
		{
            var view = View.CreateOrRetrieveViewElement ( model, null, context) ;
			Bind.SetBinding(model, view, context);

			var window = view as System.Windows.Window;
			if (window == null)
			{
				window = new System.Windows.Window
				{
					Content = view,
					SizeToContent = SizeToContent.WidthAndHeight
				};

				window.SetValue( View.IsCreatedProperty, true );
			}

			// infers the owner of this window .
			var win = Application.Current.Windows.OfType<System.Windows.Window>()
												.Where(x => x.IsActive).FirstOrDefault();

			win = win ?? Application.Current.MainWindow;
			win = win == window ? null : win;

			if ( win != null )
			{
				window.Owner = win;
				// todo: properly position the window .
				// window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
			}
			else 
			{
				// window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
			}

			var hasDisplayText = model as IUIDisplayText;
			if ( hasDisplayText != null && !ConventionManager.HasBinding( view, System.Windows.Window.TitleProperty) )
			{
				var binding = new Binding("DisplayText") { Mode = BindingMode.TwoWay };
				view.SetBinding(System.Windows.Window.TitleProperty, binding);
			}

			View.ApplyViewSettings ( view, settings );

			// extra handling .
            new WinLink ( model, window );

            return window;
        }

		/// <summary>
		///		Creates a popup for hosting a popup window.
		/// </summary>
		protected virtual Popup CreatePopup ( object model, IDictionary<string, object> settings)
		{
			var popup = new Popup();

			if ( View.ApplyViewSettings(popup, settings) )
			{
				if (!settings.ContainsKey("PlacementTarget") && !settings.ContainsKey("Placement"))
					popup.Placement = PlacementMode.MousePoint;
				if (!settings.ContainsKey("AllowsTransparency"))
					popup.AllowsTransparency = true;
			}
			else
			{
				popup.AllowsTransparency = true;
				popup.Placement = PlacementMode.MousePoint;
			}

			return popup;
		}



		/// <summary>
        ///		Creates the page.
        /// </summary>
        public virtual Page CreatePage ( object model, object context, IDictionary<string, object> settings)
		{
            var view = View.CreateOrRetrieveViewElement( model, null, context);
			
			var page = view as Page;
			if ( page == null ) // makes the page created properly .
			{
				page = new Page { Content = view };
				page.SetValue(View.IsCreatedProperty, true);
			}

            var hasDisplayText = model as IUIDisplayText;
			if ( hasDisplayText != null && !ConventionManager.HasBinding( page, Page.TitleProperty))
			{
                var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
                page.SetBinding(Page.TitleProperty, binding);
            }

            View.ApplyViewSettings ( page, settings );

            var enabledDisabled = model as IUIStatus;
			if ( enabledDisabled != null )
			{
                enabledDisabled.SetEnabled ();
				view.Unloaded += ( s, e) => enabledDisabled.SetDisabled( true);
            }

            return page ;
        }
	}


	internal class WinLink // 
	{
		/// <summary>
		///		the model for this view .
		/// </summary>
		readonly object _model;

		/// <summary>
		///		the window object for this view .
		/// </summary>
		readonly System.Windows.Window _window;

		/// <summary>
		///		if is actually closing ?
		/// </summary>
		bool _actuallyClosing;

		/// <summary>
		///		is this window disabled by view ?
		/// </summary>
		bool _disabledByView;

		/// <summary>
		///		is this window disabled by view model ?
		/// </summary>
		bool _disabledByModel;


		/// <summary>
		///		c'tor .
		/// </summary>
		public WinLink ( object model, System.Windows.Window window )
		{
			this._model = model;
			this._window = window;

			var status = model as IUIStatus;
			if (status != null)
			{
				status.SetEnabled();
				status.Disabled += this.OnDisabled;
				//
				this._window.Closed += this.OnClosed;
			}

			var gc = model as IUIGuardClose;
			if ( gc != null)
			{
				this._window.Closing += this.OnClosing;
			}
		}

		void OnClosed ( object sender, EventArgs e )
		{
			this._window.Closed -= OnClosed;
			this._window.Closing -= OnClosing;

			if (this._disabledByModel)
				return;

			var status = (IUIStatus)this._model;

			this._disabledByView = true;
			status.SetDisabled(true);
			this._disabledByView = false;
		}

		void OnClosing ( object sender, CancelEventArgs e )
		{
			if ( e.Cancel )
				return;

			if ( this._actuallyClosing )
			{
				this._actuallyClosing = false;
				return;
			}

			var gc = (IUIGuardClose)this._model;
			bool runningAsync = false, shouldClosed = false;

			gc.CanClose( canClose =>
			{
				Execution.OnUIThread(() =>
				{
					if (runningAsync && canClose)
					{
						this._actuallyClosing = true;
						this._window.Close();
					}
					else
					{
						e.Cancel = !canClose;
					}

					shouldClosed = true;
				});
			});

			if (shouldClosed)
			{
				return;
			}

			runningAsync = e.Cancel = true;
		}

		void OnDisabled ( object sender, UIDisabledEventArgs e )
		{
			if (!e.WasClosed)
				return;

			( ( IUIStatus)this._model).Disabled -= this.OnDisabled;

			if (this._disabledByView)
				return;

			this._disabledByModel = true;
			this._actuallyClosing = true;

			// close the window .
			this._window.Close ();

			this._actuallyClosing = false;
			this._disabledByModel = false;
		}
	}

}
